#include "collector.h"
#include "config.h"
#include "pmu.h"
#include "pcerrc.h"
#include <stdexcept>
#include <string>

using namespace std;

#ifdef __x86_64__

map<Metric, const char*> evtNames = {
    {Metric::L1_REF, "L1-dcache-loads"},
    {Metric::L1_MISS, "L1-dcache-load-misses"},
    {Metric::L2_REF, "rff24"},
    {Metric::L2_MISS, "r3f24"},
    {Metric::L3_REF, "LLC-loads"},
    {Metric::L3_MISS, "LLC-load-misses"},
};

#elif __aarch64__ 
map<Metric, const char*> evtNames = {
    {Metric::L1_REF, "l1d_cache"},
    {Metric::L1_MISS, "l1d_cache_refill"},
    {Metric::L2_REF, "l2d_cache"},
    {Metric::L2_MISS, "l2d_cache_refill"},
    {Metric::L3_REF, "ll_cache"},
    {Metric::L3_MISS, "ll_cache_miss"},
};
#endif

void Collector::Start(int pid)
{
    evtMetric.clear();
    vector<char*> evts;
    for (auto &evtNames : evtNames) {
        evts.push_back(const_cast<char*>(evtNames.second));
        evtMetric[evtNames.second] = evtNames.first;
    }
    int cpuList[CPU_NUMS];
    for (int i = 0;i < CPU_NUMS;++i) {
        cpuList[i] = i;
    }
    int pidList[1];
    pidList[0] = pid;
    PmuAttr attr = {0};
    attr.evtList = evts.data();
    attr.numEvt = evts.size();
    attr.cpuList = cpuList;
    attr.numCpu = CPU_NUMS;
    attr.pidList = pidList;
    attr.numPid = 1;

    pd = PmuOpen(COUNTING, &attr);
    if (pd < 0) {
        throw runtime_error("Failed to open pmu.");
    }
    PmuEnable(pd);
}

std::map<int, cache_stat> Collector::Read()
{
    PmuData *data = nullptr;
    int len = PmuRead(pd, &data);
    if (len < 0) {
        throw runtime_error("Failed to read pmu data.");
    }

    if (verbose) {
        PmuDumpData(data, len, const_cast<char*>(logpath.c_str()), 0);
    }

    map<int, cache_stat> ret;
    for (int i = 0;i < len;++i) {
        auto &pmuData = data[i];
        auto findStat = ret.find(pmuData.cpu);
        if (findStat == ret.end()) {
            cache_stat newStat = {0};
            newStat.cpu = pmuData.cpu;
            ret[pmuData.cpu] = newStat;
        }

        auto findMetric = evtMetric.find(pmuData.evt);
        if (findMetric != evtMetric.end()) {
            ret[pmuData.cpu].metrics[findMetric->second] += pmuData.count;
        }
    }

    return ret;
}
